package com.spzx.product.service.impl;

import com.spzx.common.security.annotation.GuiGuCache;
import com.spzx.product.api.domain.SkuPrice;
import com.spzx.product.api.domain.SkuQuery;
import com.spzx.product.api.domain.vo.ProductSkuVo;
import com.spzx.product.api.domain.ProductSku;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.service.IProductSkuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 商品sku 服务实现类
 * </p>
 *
 * @author lee
 * @since 2024-07-15
 */
@Slf4j
@Service
public class ProductSkuServiceImpl extends ServiceImpl<ProductSkuMapper, ProductSku> implements IProductSkuService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public List<ProductSku> getTopSale() {

        return baseMapper.getTopSale();
    }

    @Override
    public List<ProductSkuVo> getProductList(SkuQuery skuQuery) {
        return baseMapper.getProductList(skuQuery);
    }

    @Override
    @GuiGuCache(prefix = "SkuPrice:")
    public SkuPrice getSkuPrice(Integer skuId) {
        return baseMapper.getSkuPrice(skuId);
    }

    @Override
    public ProductSku getProductSku(Integer skuId) {

        try {
            //定义key
            String dataKey = "productSku:" + skuId;

            //查看缓存中是否有这个数据
            ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
            if (productSku != null) {
                log.info("命中缓存直接返回,线程名:{},线程id:{}", Thread.currentThread().getName(), Thread.currentThread().getId());
                return productSku;
            }

            //缓存中没有则使用分布式锁在数据库中查询
            String LockKey = "productSku:" + "lock";
            String LockValue = UUID.randomUUID().toString().replaceAll("-", "");
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(LockKey, LockValue, 3, TimeUnit.SECONDS);
            if (flag) {
                try {
                    //获取到锁
                    log.info("获取锁成功：线程名:{},线程id:{}", Thread.currentThread().getId(), Thread.currentThread().getName());
                    productSku = baseMapper.selectById(skuId);
                    long ttl = productSku == null ? 1 * 60 : 5 * 60;
                    redisTemplate.opsForValue().set(dataKey, productSku,ttl, TimeUnit.SECONDS);
                    return productSku;
                } finally {
                    String script = """
                            if 
                                redis.call('get', KEYS[1]) == ARGV[1] 
                            then 
                                return redis.call('del', KEYS[1]) 
                            else 
                                return 0 
                            end
                            """;
                    redisTemplate.execute(new DefaultRedisScript(script, Boolean.class), Arrays.asList(LockKey), LockValue);

                }
            } else {
                //没获取到锁
                log.info("获取锁失败,自旋：线程名:{},线程id:{}", Thread.currentThread().getId(), Thread.currentThread().getName());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return this.getProductSku(skuId);
            }
        } catch (RuntimeException e) {
            log.error("[商品服务]查询商品信息异常:{}", e);
            return baseMapper.selectById(skuId);
        }
    }

    @Override
    @GuiGuCache(prefix = "SkuPrice:")
    public List<SkuPrice> getSkuPriceList(List<Long> skuIdList) {
        List<SkuPrice> skuPriceList = baseMapper.getSkuPriceList(skuIdList);
        return skuPriceList;
    }
}
